package bg.btanev.eightyfour.domain.profile;

import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;

import bg.btanev.eightyfour.domain.Tag;
import bg.btanev.eightyfour.domain.entitybase.DomainEntity;
import bg.btanev.eightyfour.domain.geo.GeoLocation;

/**
 *
 * @author B.Tanev
 *
 */
public abstract class ProfileEvent extends DomainEntity<Long> implements Comparable<ProfileEvent> {
	private static final int MAX_DESCRIPTION_LENGHT = 5000;
	private static final String CAPTION_NO_TITLE = "No title";

	/*
	 * this is an entity because a search could be executed over all of the profile events
	 */

	/**
	 *
	 */
	private static final long serialVersionUID = 7712447856757501787L;

	private Date date;
	private String title;
	private String description;
	private Set<Tag> tags = new HashSet<Tag>();

	private GeoLocation geoLocation; // component

	private Profile<? extends ProfileEvent> profile; // reference to parent profile; many-to-one;

	protected ProfileEvent() {
		super();
	}

	public ProfileEvent(final Date date, final Profile<? extends ProfileEvent> profile) {
		this(null, date, profile, null, null, null);
	}

	public ProfileEvent(final Date date, final String title, final Profile<? extends ProfileEvent> profile,
			final String description, final GeoLocation geoLocation) {

		this(null, date, profile, title, description, geoLocation);
	}

	public ProfileEvent(final Long id, final Date date, final Profile<? extends ProfileEvent> profile, String title,
			final String description, final GeoLocation geoLocation) {

		super(id);

		Validate.notNull(date, "Profile date event cannot be null.");
		Validate.notNull(profile, "Profile cannot be null");

		if (StringUtils.isEmpty(title)) {
			title = CAPTION_NO_TITLE;
		}

		// Validate.notEmpty(title, "Profile title event cannot be empty.");
		// unfortunately a description element could also be empty.
		// Validate.notEmpty(description, "Profile description event cannot be empty.");

		this.date = date;
		this.title = title;
		if (description != null && description.length() > 0) {
			setDescription(description);
		}
		this.geoLocation = geoLocation;

		updateProfile(profile);
	}

	private void setDescription(String description) {
		assert (description != null);

		if (description.length() >= MAX_DESCRIPTION_LENGHT) {
			description = description.substring(0, MAX_DESCRIPTION_LENGHT - 4) + "...";
		}
		this.description = description;
	}

	public Date getDate() {
		return date;
	}

	public String getTitle() {
		return title;
	}

	public String getDescription() {
		return description;
	}

	public GeoLocation getGeoLocation() {
		return geoLocation;
	}

	public boolean addTag(final Tag tag) {
		Validate.notNull(tag, "Tag cannot be null");

		return tags.add(tag);
	}

	public boolean addTags(final Collection<Tag> tags) {
		Validate.notNull(tags, "tags collection cannot be null");

		return this.tags.addAll(tags);
	}

	public boolean removeTag(final Tag tag) {
		Validate.notNull(tag, "Tag cannot be null");

		return tags.remove(tag);
	}

	public Set<Tag> getTags() {
		return Collections.unmodifiableSet(tags);
	}

	public Profile<? extends ProfileEvent> getProfile() {
		return profile;
	}

	private boolean updateProfile(Profile<? extends ProfileEvent> profile) {
		Validate.notNull(profile);

		if (this.profile != null) {
			this.profile.removeProfileEvent(this);
		}

		this.profile = profile;
		return true;//this.profile.addProfileEvent(this);
	}

	@Override
	public String toString() {
		return "ProfileEvent [date=" + date + ", description=" + description + ", title=" + title + ", toString()="
				+ super.toString() + "]";
	}

	@Override
	public int compareTo(ProfileEvent o) {
		return this.date.compareTo(o.date);
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((date == null) ? 0 : date.hashCode());
		result = prime * result + ((description == null) ? 0 : description.hashCode());
		result = prime * result + ((geoLocation == null) ? 0 : geoLocation.hashCode());
		result = prime * result + ((title == null) ? 0 : title.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		ProfileEvent other = (ProfileEvent) obj;
		if (date == null) {
			if (other.date != null)
				return false;
		} else if (!date.equals(other.date))
			return false;
		if (description == null) {
			if (other.description != null)
				return false;
		} else if (!description.equals(other.description))
			return false;
		if (geoLocation == null) {
			if (other.geoLocation != null)
				return false;
		} else if (!geoLocation.equals(other.geoLocation))
			return false;
		if (title == null) {
			if (other.title != null)
				return false;
		} else if (!title.equals(other.title))
			return false;
		return true;
	}
}
